/**
 * Implementation of the Frame interface.
 *
 * @author Timothy Flynn (trflynn@wpi.edu)
 * @version April 12, 2012
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <string>
using std::string;

#include "Frame.h"

/**
 * Default constructor. Leave all fields blank?
 */
Frame::Frame() {
	// TODO
}

/**
 * Constructor from a string containing all information.
 */
Frame::Frame(char *str) {
	// Byte 0 is 0x7E - ignore for now?
	string *s = new string(str);
	str[1] == '0' ? this->end = false : this->end = true;
	this->seq = (short)atoi(s->substr(2, SEQ_NUM_LEN).c_str());
	this->ack = (short)atoi(s->substr(7, ACK_NUM_LEN).c_str());
	this->crc = (unsigned short)atoi(s->substr(12, CRC_LEN).c_str());
	this->length = (short)atoi(s->substr(17, DATA_LEN).c_str());
	this->data = (char *)malloc((this->length + 1) * sizeof(char));
	memcpy(this->data, (char *)s->substr(20, this->length).c_str(), this->length);
	this->data[this->length] = '\0';
	// Last byte is 0x7E - ignore for now?
}

/**
 * Destructor.
 */
Frame::~Frame() {
	// TODO
}

/**
 * Convert this Frame to a string.
 */
char *Frame::toString() {
	char *str = (char *)malloc((FRAME_LEN + this->length + 1) * sizeof(char));
	str[0] = 0x7E;
	this->end ? memcpy(str+1, "1", 1) : memcpy(str+1, "0", 1);
	memcpy(str+2, stoa(this->seq, SEQ_NUM_LEN), SEQ_NUM_LEN);
	memcpy(str+7, stoa(this->ack, ACK_NUM_LEN), ACK_NUM_LEN);
	memcpy(str+12, ustoa(this->crc, CRC_LEN), CRC_LEN);
	memcpy(str+17, stoa(this->length, DATA_LEN), DATA_LEN);
	memcpy(str+20, this->data, this->length);
	str[20+this->length] = 0x7E;
	str[20+this->length+1] = '\0';

	return str;
}

/**
 * Stuff a frame using PPP byte stuffing.
 */
void Frame::stuff() {
	string *s = new string(this->data);

	// Iterate through data looking for 0x7E/0x7D
	for(int i=0; i<this->length; i++) {
		if(s->at(i) == 0x7E) { // stuff 0x5E
			s->insert(i+1, 1, 0x5E);
			this->length++;
		} else if(s->at(i) == 0x7D) { // stuff 0x5D
			s->insert(i+1, 1, 0x5D);
			this->length++;
		}
	}

	// Copy stuffed string into this->data
	this->data = (char *)malloc((this->length+1) * sizeof(char));
	memcpy(this->data, (char *)s->c_str(), this->length);
	this->data[this->length] = '\0';
}

/**
 * Unstuff a frame using PPP byte stuffing.
 */
void Frame::unstuff() {
	string *s = new string(this->data);

	// Iterate through data looking for 0x7E/0x7D
	for(int i=0; i<this->length; i++) {
		if(s->at(i) == 0x7E) { // unstuff 0x5E
			s->erase(i+1, 1);
			this->length--;
		} else if(s->at(i) == 0x7D) { // unstuff 0x5D
			s->erase(i+1, 1);
			this->length--;
		}
	}

	// Copy unstuffed string into this->data
	this->data = (char *)malloc((this->length+1) * sizeof(char));
	memcpy(this->data, (char *)s->c_str(), this->length);
	this->data[this->length] = '\0';
}
